<?php


namespace app\api\controller;

class ApiBase extends \app\common\Controller
{
	public $project = [];
	public $page_no = 1;
	public $page_size = 10;
	public $input = [];
	public $apiInfo = [];
	public $apiConfig = [];
	public $encryption_config;
	private $agreement;
	private $agreementService;
	public $client_ip;
	public $original_input;
	protected $apiCode = [];
	public function __construct($agreement = "http", $app = [], $api_config = [], $api = [], $client_ip = "", $input = [], $agreementService = null)
	{
		parent::__construct();
		$this->agreement = $agreement;
		$this->agreementService = $agreementService;
		if ($this->agreement == "http") {
			header("content-type:application/json;charset=utf-8");
			if (empty($api)) {
				$input = input();
				if (!isset($input["appid"]) || $input["appid"] == "") {
					$this->response(Code::fail, "缺少参数appid");
				}
				$app = \think\Db::name("app")->where(["id" => $input["appid"]])->find();
				$controller_tmp = explode(".", request()->controller());
				$controller = "";
				if (is_array($controller_tmp) && count($controller_tmp) == 2) {
					$controller .= $controller_tmp[0] . "." . parseCamel($controller_tmp[1], true);
				} else {
					$controller .= parseCamel($controller_tmp[0], true);
				}
				$api = \think\Db::name("app_api")->where(["controller" => $controller, "method" => request()->action()])->find();
			}
			$this->client_ip = request()->ip();
		} else {
			if ($this->agreement == "tcp" || $this->agreement == "udp") {
				$this->client_ip = $client_ip;
			}
		}
		if (empty($app)) {
			$this->response(Code::fail, "应用不存在");
		}
		$code_list = \think\Db::name("app_api_code")->select();
		$apiCode = [];
		foreach ($code_list as $code_item) {
			$apiCode[$code_item["id"]] = ["id" => $code_item["id"], "message" => $code_item["message"]];
		}
		$this->apiCode = $apiCode;
		$this->original_input = $this->input;
		$this->project = $app;
		if (empty($api)) {
			$this->response(10003, "请求接口不存在");
		}
		$this->apiInfo = $api;
		if (empty($api_config)) {
			$api_config = \think\Db::name("app_api_config")->where(["appid" => $app["id"], "api_id" => $api["id"]])->find();
		}
		$this->apiConfig = $api_config ?? [];
		$encryption_config = getEncryptionConfig(json_decode($this->project["encryption_config"], true) ?? [], $this->apiConfig ? json_decode($this->apiConfig["encryption_config"], true) ?? [] : []);
		$this->encryption_config = $encryption_config;
		if ($this->agreement === "http") {
			if (isset($api_config["method"]) && $api_config["method"] == "get") {
				$input = input("get.");
			} elseif (isset($api_config["method"]) && $api_config["method"] == "post") {
				$input = input("post.");
			} else {
				if (empty($api_config["method"]) || $api_config["method"] == "default") {
					$input = input("post.");
					if (empty($input)) {
						$input = input("get.");
					}
				}
			}
		}
		$input = $this->input_decrypt($input);
		if (!!$encryption_config->getApiTransferSignatureType()) {
			if (!(isset($input["signature"]) && isset($input["timestamp"])) || $input["signature"] == "" || $input["timestamp"] == "") {
				$this->response(10004, "缺少参数signature or timestamp");
			}
			if ($encryption_config->isApiTransferSignatureTimeLimit()) {
				$allow_time_scope_minimum = time() - $encryption_config->getApiTransferSignatureTimeLimitMinimum();
				$allow_time_scope_maximum = time() + $encryption_config->getApiTransferSignatureTimeLimitMaximum();
				if ($allow_time_scope_maximum < $input["timestamp"] || $input["timestamp"] < $allow_time_scope_minimum) {
					$this->response(10005, "时差校验不通过");
				}
			}
			if ($encryption_config->getApiTransferSignatureType() == 1 && md5($this->kSortStr($input) . $this->project["appkey"]) != $input["signature"]) {
				$this->response(10006, "签名校验失败");
			}
			if ($encryption_config->getApiTransferSignatureType() == 2 && sha1($this->kSortStr($input) . $this->project["appkey"]) != $input["signature"]) {
				$this->response(10006, "签名校验失败");
			}
		}
		if (!empty($api) && isset($api_config["params"])) {
			$params = [];
			$api_params = json_decode($api["param"], true);
			$api_config["params"] = json_decode($api_config["params"], true);
			foreach ($api_params as $param) {
				if (!empty($api_config["params"][$param["field"]])) {
					if (!empty($input[$api_config["params"][$param["field"]]])) {
						$params[$param["field"]] = $input[$api_config["params"][$param["field"]]];
						unset($input[$api_config["params"][$param["field"]]]);
					} else {
						unset($input[$param["field"]]);
					}
				}
			}
			$input = array_merge($input, $params);
		}
		if (isset($input["page"]) && $input["page"] != "") {
			$this->page_no = $input["page"];
		}
		if (isset($input["limit"]) && $input["limit"] != "") {
			$this->page_size = $input["limit"];
		}
		$this->input = $input;
		if (!empty($api_config["disable"])) {
			$this->response(10007, "当前接口被禁止调用");
		}
		if ($encryption_config->isApiDataTransferVerifyCaptcha() && $api["id"] != 31) {
			if (!isset($input["captcha_value"])) {
				$this->response(10008, "缺少图形验证码参数captcha_value");
			}
			if (!captcha_check($input["captcha_value"], "api_" . $this->project["id"])) {
				$this->response(10009, "图形验证码验证失败");
			}
		}
		if ($encryption_config->isApiRequestVerifyBlacklist()) {
			$ip = $this->client_ip;
			if (\think\Db::name("app_blacklist")->where(["appid" => $this->project["id"], "type" => 0, "value" => $ip])->count() > 0) {
				$this->response(10010, "您的网络环境异常");
			}
			if (!empty($this->input["machine_code"]) && \think\Db::name("app_blacklist")->where(["appid" => $this->project["id"], "type" => 1, "value" => $this->input["machine_code"]])->count() > 0) {
				$this->response(10011, "您的机器设备异常");
			}
		}
		define("project", $this->project);
		define("apiInfo", $this->apiInfo);
		define("apiConfig", $this->apiConfig);
		define("client_ip", $this->client_ip);
	}
	public function input_decrypt($input)
	{
		$encryption_config = $this->encryption_config;
		$_input = ["appid" => $this->project["id"]];
		if ($encryption_config->getApiDataTransferEncryptionType() != 0) {
			foreach ($input as $k => $v) {
				if ($k != "appid") {
					try {
						if ($encryption_config->getApiDataTransferEncryptionType() == 1 && $encryption_config->getConfigAesKey()) {
							$mode = "AES-128-ECB";
							$iv = "";
							if ($encryption_config->getConfigAesMode() == 1) {
								$mode = "AES-192-ECB";
							} elseif ($encryption_config->getConfigAesMode() == 2) {
								$mode = "AES-256-ECB";
							} else {
								if (in_array($encryption_config->getConfigAesMode(), [3, 4, 5])) {
									$mode = "AES-128-CBC";
									$iv = $encryption_config->getConfigAesIv();
									if ($encryption_config->getConfigAesMode() == 4) {
										$mode = "AES-192-CBC";
									} elseif ($encryption_config->getConfigAesMode() == 5) {
										$mode = "AES-256-CBC";
									}
								}
							}
							if ($encryption_config->isParamValueTransferEncryption()) {
								$v = openssl_decrypt($this->input_decrypt_decode($v), $mode, $encryption_config->getConfigAesKey(), OPENSSL_RAW_DATA, $iv);
							}
							if ($encryption_config->isParamNameTransferEncryption()) {
								$k = openssl_decrypt($this->input_decrypt_decode($k), $mode, $encryption_config->getConfigAesKey(), OPENSSL_RAW_DATA, $iv);
							}
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 2 && $encryption_config->getConfigRc4Key()) {
							if ($encryption_config->isParamValueTransferEncryption()) {
								$v = rc4($encryption_config->getConfigRc4Key(), $this->input_decrypt_decode($v));
							}
							if ($encryption_config->isParamNameTransferEncryption()) {
								$k = rc4($encryption_config->getConfigRc4Key(), $this->input_decrypt_decode($k));
							}
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 3 && $encryption_config->getConfigBase64Key()) {
							if ($encryption_config->isParamValueTransferEncryption()) {
								$base64 = new \base64(str_split($encryption_config->getConfigBase64Key()));
								$v = $base64->decode($this->input_decrypt_decode($v));
							}
							if ($encryption_config->isParamNameTransferEncryption()) {
								$base64 = new \base64(str_split($encryption_config->getConfigBase64Key()));
								$k = $base64->decode($this->input_decrypt_decode($k));
							}
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 4 && $encryption_config->getConfigAesKey()) {
							$mode = "DES-ECB";
							if ($encryption_config->getConfigDesMode() == 1) {
							}
							if ($encryption_config->isParamValueTransferEncryption()) {
								$v = openssl_decrypt($this->input_decrypt_decode($v), $mode, $encryption_config->getConfigDesKey(), OPENSSL_RAW_DATA);
							}
							if ($encryption_config->isParamNameTransferEncryption()) {
								$k = openssl_decrypt($this->input_decrypt_decode($k), $mode, $encryption_config->getConfigDesKey(), OPENSSL_RAW_DATA);
							}
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 5) {
							$rsa = new \app\common\utils\Rsa("", $encryption_config->getConfigRsaClientPrivateKey(), $encryption_config->getConfigRsaPkcs());
							if ($encryption_config->isParamValueTransferEncryption()) {
								$v = $rsa->decrypt($this->input_decrypt_decode($v));
							}
							if ($encryption_config->isParamNameTransferEncryption()) {
								$k = $rsa->decrypt($this->input_decrypt_decode($k));
							}
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 775294852 && $encryption_config->getConfigCustomDecryptFunction()) {
							if (!function_exists("eval")) {
							}
							$inhibit_eval_keyword = ["common/system", "eval(", "eval (", "\\common\\system"];
							foreach ($inhibit_eval_keyword as $inhibit_eval_keyword_item) {
								if (strpos($encryption_config->getConfigCustomDecryptFunction(), $inhibit_eval_keyword_item) !== false) {
									$this->response(Code::error, "数自定义算法内存在危险代码，请更改");
								}
							}
							if (!function_exists("apiDataTransferEncryptionDecryptFunction")) {
								eval($encryption_config->getConfigCustomDecryptFunction());
								if (!function_exists("apiDataTransferEncryptionDecryptFunction")) {
									$this->response(Code::error, "自定义算法未找到apiDataTransferEncryptionDecryptFunction函数");
								}
							}
							if ($encryption_config->isParamValueTransferEncryption()) {
								$v = apiDataTransferEncryptionDecryptFunction($this->project, $this->apiInfo, $this->apiConfig, $encryption_config, $input, $this->input_decrypt_decode($k));
							}
							if ($encryption_config->isParamNameTransferEncryption()) {
								$k = apiDataTransferEncryptionDecryptFunction($this->project, $this->apiInfo, $this->apiConfig, $encryption_config, $input, $this->input_decrypt_decode($k));
							}
						}
						$_input[trim($k)] = $v;
					} catch (\Exception $e) {
						$this->response(Code::error, "数据解密失败，请检查提交的数据 " . $e->getMessage());
					}
				}
			}
		} else {
			$_input = $input;
		}
		return $_input;
	}
	private function input_decrypt_decode($value)
	{
		$encryption_config = $this->encryption_config;
		try {
			if (!$encryption_config->getApiDataTransferEncryptionEncodedType()) {
				$value = base64_decode($value);
			} else {
				if ($encryption_config->getApiDataTransferEncryptionEncodedType() == 1) {
					try {
						$value = hex2bin($value);
					} catch (\Exception $e) {
						$value = hexToStr($value);
					}
				}
			}
		} catch (\Exception $e) {
			$this->response(Code::error, "数据解密转换解码失败:" . $e->getMessage());
		}
		return $value;
	}
	public function response($code, $message = "", $data = [], ...$other)
	{
		if ($code != Code::success) {
			\think\Db::rollback();
		}
		if ($this->project) {
			$data["moreOtherData"] = ["api_extra_data" => $this->apiConfig["extra_data"] ?? "", "request_safe_code" => $this->input["request_safe_code"] ?? ""];
			$api_code_config = json_decode($this->project["api_code_config"], true) ?? [];
			if (!empty($api_code_config[$code]["message"])) {
				$message = $api_code_config[$code]["message"];
			}
			if (!empty($api_code_config[$code]["id"])) {
				$code = $api_code_config[$code]["id"];
			}
		}
		$response = ["code" => $code, "message" => $message, "timestamp" => time(), "nonceStr" => md5(random_string())];
		foreach ($other as $array) {
			$response[$array[0]] = $array[1];
		}
		$encryption_config = $this->encryption_config;
		if ($this->project && $encryption_config) {
			if ($this->agreement === "tcp" || $this->agreement === "udp") {
				$response["apiInfo"] = $this->apiInfo ? json_encode(["id" => $this->apiInfo["id"], "name" => $this->apiInfo["name"], "controller" => $this->apiInfo["controller"], "method" => $this->apiInfo["method"], "api" => $this->apiInfo["api"]], JSON_UNESCAPED_UNICODE) : null;
			}
			$response["data"] = api_response_data_format_handle($encryption_config, $data);
			if ($encryption_config->isApiResponseEncryption()) {
				if (!!$encryption_config->getApiDataTransferEncryptionType()) {
					try {
						if ($encryption_config->getApiDataTransferEncryptionType() == 1 && $encryption_config->getConfigAesKey()) {
							$mode = "AES-128-ECB";
							$iv = "";
							if ($encryption_config->getConfigAesMode() == 1) {
								$mode = "AES-192-ECB";
							} elseif ($encryption_config->getConfigAesMode() == 2) {
								$mode = "AES-256-ECB";
							} else {
								if (in_array($encryption_config->getConfigAesMode(), [3, 4, 5])) {
									$mode = "AES-128-CBC";
									$iv = $encryption_config->getConfigAesIv();
									if ($encryption_config->getConfigAesMode() == 4) {
										$mode = "AES-192-CBC";
									} elseif ($encryption_config->getConfigAesMode() == 5) {
										$mode = "AES-256-CBC";
									}
								}
							}
							$dataEncrypt = openssl_encrypt($response["data"], $mode, $encryption_config->getConfigAesKey(), OPENSSL_RAW_DATA, $iv);
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 2 && $encryption_config->getConfigRc4Key()) {
							$dataEncrypt = rc4($encryption_config->getConfigRc4Key(), $response["data"]);
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 3 && $encryption_config->getConfigBase64Key()) {
							$base64 = new \base64(str_split($encryption_config->getConfigBase64Key()));
							$dataEncrypt = $base64->encode($response["data"]);
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 4 && $encryption_config->getConfigDesKey()) {
							$mode = "DES-ECB";
							if ($encryption_config->getConfigDesMode() == 1) {
							}
							$dataEncrypt = openssl_encrypt($response["data"], $mode, $encryption_config->getConfigDesKey(), OPENSSL_RAW_DATA);
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 5) {
							$rsa = new \app\common\utils\Rsa($encryption_config->getConfigRsaServicePublicKey(), "", $encryption_config->getConfigRsaPkcs());
							$dataEncrypt = $rsa->encrypt($response["data"]);
						} elseif ($encryption_config->getApiDataTransferEncryptionType() == 775294852 && $encryption_config->getConfigCustomEncryptFunction()) {
							if (!function_exists("eval")) {
							}
							$inhibit_eval_keyword = ["common/system", "eval(", "eval (", "\\common\\system"];
							foreach ($inhibit_eval_keyword as $inhibit_eval_keyword_item) {
								if (strpos($encryption_config->getConfigCustomEncryptFunction(), $inhibit_eval_keyword_item) !== false) {
									throw new \Exception("自定义算法内存在危险代码，请更改");
								}
							}
							if (!function_exists("apiDataTransferEncryptionEncryptFunction")) {
								eval($encryption_config->getConfigCustomEncryptFunction());
								if (!function_exists("apiDataTransferEncryptionEncryptFunction")) {
									throw new \Exception("自定义算法未找到apiDataTransferEncryptionEncryptFunction函数");
								}
								$dataEncrypt = apiDataTransferEncryptionEncryptFunction($this->project, $this->apiInfo, $this->apiConfig, $encryption_config, $response["data"]);
							}
						}
						if (!$encryption_config->getApiDataTransferEncryptionEncodedType()) {
							$response["data"] = base64_encode($dataEncrypt);
						} else {
							if ($encryption_config->getApiDataTransferEncryptionEncodedType() == 1) {
								$response["data"] = bin2hex($dataEncrypt);
							}
						}
					} catch (\Exception $e) {
						$response["message"] = "系统错误,响应数据加密处理失败：" . $e->getMessage();
						$response["code"] = Code::error;
						$response["data"] = "";
					}
				}
			}
			$signatureString = "";
			if (!$encryption_config->getApiResponseSignatureParamStringCalculationRule()) {
				$signatureString = $response["data"] . $this->project["appkey"];
			} else {
				if ($encryption_config->getApiResponseSignatureParamStringCalculationRule() == 1) {
					$signatureString = $this->kSortStr($response) . $this->project["appkey"];
				} elseif ($encryption_config->getApiResponseSignatureParamStringCalculationRule() == 2) {
					$signatureString = $response["data"] . $response["timestamp"] . $this->project["appkey"];
				}
			}
			if (!$encryption_config->getApiResponseSignatureType()) {
				$response["signature"] = md5($signatureString);
			} else {
				if ($encryption_config->getApiResponseSignatureType() == 1) {
					$response["signature"] = sha1($signatureString);
				}
			}
		} else {
			$response["signature"] = "";
		}
		if ($this->project && $encryption_config) {
			$responseData = api_response_data_format_handle($encryption_config, $response);
		} else {
			$responseData = json_encode($response);
		}
		if ($encryption_config) {
			if ($encryption_config->isApiRequestForwardingOpen() && $encryption_config->getApiRequestForwardingUrl()) {
				if (true) {
					$RequestForwardingData = ["requestIp" => $this->client_ip, "requestUrl" => request()->url(true), "requestMethod" => request()->method(), "requestCookie" => request()->cookie(), "requestHeader" => json_encode(request()->header()), "requestParam" => ["original" => $this->original_input, "processed" => $this->input], "responseData" => $response, "apiBusinessData" => $data];
					$RequestForwardingResult = json_post($encryption_config->getApiRequestForwardingUrl(), $RequestForwardingData);
					if ($RequestForwardingResult && $encryption_config->isApiRequestForwardingResponseDataTakeoverOpen()) {
						$responseData = $RequestForwardingResult;
					}
				}
			}
		}
		if ($this->agreement === "tcp") {
			$this->agreementService["server"]->send($this->agreementService["fd"], $responseData);
		} elseif ($this->agreement === "udp") {
			$this->agreementService["server"]->sendto($this->agreementService["clientInfo"]["address"], $this->agreementService["clientInfo"]["port"], $responseData);
		} else {
			echo $responseData;
		}
		exit;
	}
	public function kSortStr($paramArr)
	{
		if (empty($paramArr)) {
			return "";
		}
		ksort($paramArr);
		$str = "";
		foreach ($paramArr as $k => $v) {
			if ($v != "" && $k != "signature" && $k != "appid" && $k != "debug") {
				$str .= $k . "=" . $v . "&";
			}
		}
		return rtrim($str, "&");
	}
}